package cn.net.sinodata.system.service.impl;

import cn.hutool.core.net.NetUtil;
import cn.net.sinodata.common.core.enums.LockKeyEnum;
import cn.net.sinodata.system.domain.BmDistributedLock;
import cn.net.sinodata.system.mapper.BmDistributedLockMapper;
import cn.net.sinodata.system.service.IBmDistributedLockService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.net.InetAddress;
import java.util.Date;

/**
 * 交易日志Service业务层处理
 *
 * @author dongshaoyang
 * @date 2025-10-31
 */
@Slf4j
@Service
public class BmDistributedLockServiceImpl implements IBmDistributedLockService {

    // 默认锁过期时间（单位毫秒）
    public static final long DEFAULT_EXPIRE_MILLIS = 10000L;

    @Autowired
    private BmDistributedLockMapper bmDistributedLockMapper;

    /**
     * 尝试获取分布式锁（立即返回）
     * @param lockKey 锁标识
     * @return
     */
    @Override
    public boolean tryLock(LockKeyEnum lockKey) {
        return tryLock(lockKey, DEFAULT_EXPIRE_MILLIS);
    }

    /**
     * 尝试获取分布式锁（立即返回）
     * @param lockKey 锁标识
     * @param expireTimeMillis 锁过期时间（单位毫秒）
     * @return
     */
    @Override
    public boolean tryLock(LockKeyEnum lockKey, long expireTimeMillis) {
        log.info("尝试加锁-开始");
        Date expireTime = new Date(System.currentTimeMillis() + expireTimeMillis);
        try {
            BmDistributedLock bmDistributedLock = new BmDistributedLock();
            bmDistributedLock.setLockKey(lockKey.code);
            bmDistributedLock.setLockHold(genLockHold());
            bmDistributedLock.setCreateTime(new Date());
            bmDistributedLock.setExpireTime(expireTime);
            int count = bmDistributedLockMapper.insert(bmDistributedLock);
            log.info("尝试加锁-结束");
            return count > 0;
        } catch (DuplicateKeyException e) {
            log.info("尝试加锁失败，锁已存在");
            return false;
        } catch (Exception e) {
            log.error("尝试加锁失败:", e);
            return false;
        }
    }

    /**
     * 尝试获取分布式锁（带等待时间）
     * @param lockKey 锁标识
     * @param waitTimeMillis 等待时间（单位毫秒）
     * @return
     */
    @Override
    public boolean lockWithWait(LockKeyEnum lockKey, long waitTimeMillis) {
        return lockWithWait(lockKey, DEFAULT_EXPIRE_MILLIS, waitTimeMillis);
    }

    /**
     * 尝试获取分布式锁（带等待时间）
     * @param lockKey 锁标识
     * @param expireTimeMillis 锁过期时间（单位毫秒）
     * @param waitTimeMillis 等待时间（单位毫秒）
     * @return
     */
    @Override
    public boolean lockWithWait(LockKeyEnum lockKey, long expireTimeMillis, long waitTimeMillis) {
        long startMillis = System.currentTimeMillis();
        while (true) {
            if (tryLock(lockKey, expireTimeMillis)) {
                return true;
            }
            // 检查是否超时
            if (System.currentTimeMillis() - startMillis >= waitTimeMillis) {
                return false;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                log.error("获取锁被中断:", e);
                Thread.currentThread().interrupt();
                throw new RuntimeException("获取锁被中断", e);
            }
        }
    }

    /**
     * 获取分布式锁（阻塞式）
     * @param lockKey 锁标识
     * @return
     */
    @Override
    public void lock(LockKeyEnum lockKey) {
        lock(lockKey, DEFAULT_EXPIRE_MILLIS);
    }

    /**
     * 获取分布式锁（阻塞式）
     * @param lockKey 锁标识
     * @param expireTimeMillis 超时时间（单位毫秒）
     * @return
     */
    @Override
    public void lock(LockKeyEnum lockKey, long expireTimeMillis) {
        log.info("加锁-开始");
        while (true) {
            if (tryLock(lockKey, expireTimeMillis)) {
                log.info("加锁-结束");
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                log.error("获取锁被中断:", e);
                Thread.currentThread().interrupt();
                throw new RuntimeException("获取锁被中断", e);
            }
        }
    }

    /**
     * 释放分布式锁
     * @param lockKey 锁标识
     * @return
     */
    @Override
    public boolean unlock(LockKeyEnum lockKey) {
        log.info("尝试解锁-开始");
        int delete = bmDistributedLockMapper.delete(new QueryWrapper<BmDistributedLock>().eq("lock_key", lockKey.code).eq("lock_hold", genLockHold()));
        log.info("尝试解锁-结束，解锁数量：{}", delete);
        return delete>0;
    }

    /**
     * 清理过期锁
     */
    public void cleanExpiredLock() {
        log.info("清理过期的锁-开始");
        int count = bmDistributedLockMapper.delete(new QueryWrapper<BmDistributedLock>().lt("expire_time", new Date()));
        log.info("清理过期的锁-结束-", count);
    }

    /**
     * 生成LockHold
     * @return
     */
    private String genLockHold() {
        InetAddress localhost = NetUtil.getLocalhost();
        long threadId = Thread.currentThread().getId();
        return localhost.getHostAddress() + "-" + threadId;
    }
}
